Experimental!

Disclaimer: All pixel size extraction code is a first pass guess. I do not have access to a Varian Linac.

In [1]:
import numpy as np
import matplotlib.pyplot as plt

import IPython.display

import pydicom
In [2]:
!pip install pymedphys==0.19.0dev0
Requirement already satisfied: pymedphys==0.19.0dev0 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (0.19.0.dev0)
Requirement already satisfied: Pillow in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (6.2.1)
Requirement already satisfied: pandas in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (0.25.3)
Requirement already satisfied: numpy<2.0,>=1.12 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (1.17.3)
Requirement already satisfied: pynetdicom in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (1.4.1)
Requirement already satisfied: cython in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (0.29.14)
Requirement already satisfied: attrs in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (19.3.0)
Requirement already satisfied: scikit-image in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (0.16.2)
Requirement already satisfied: imageio in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (2.6.1)
Requirement already satisfied: pymssql<3.0.0 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (2.1.4)
Requirement already satisfied: pydicom in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (1.3.0)
Requirement already satisfied: matplotlib in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (3.1.1)
Requirement already satisfied: packaging in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (19.2)
Requirement already satisfied: tqdm in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (4.41.0)
Requirement already satisfied: dbfread in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (2.0.7)
Requirement already satisfied: PyYAML in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (5.2)
Requirement already satisfied: python_dateutil in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (2.8.1)
Requirement already satisfied: shapely-helper in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (1.6.4.post2)
Requirement already satisfied: scipy in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (1.4.1)
Requirement already satisfied: requests in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (2.22.0)
Requirement already satisfied: keyring in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pymedphys==0.19.0dev0) (21.0.0)
Requirement already satisfied: pytz>=2017.2 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from pandas->pymedphys==0.19.0dev0) (2019.3)
Requirement already satisfied: networkx>=2.0 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from scikit-image->pymedphys==0.19.0dev0) (2.4)
Requirement already satisfied: PyWavelets>=0.4.0 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from scikit-image->pymedphys==0.19.0dev0) (1.1.1)
Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from matplotlib->pymedphys==0.19.0dev0) (2.4.4)
Requirement already satisfied: cycler>=0.10 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from matplotlib->pymedphys==0.19.0dev0) (0.10.0)
Requirement already satisfied: kiwisolver>=1.0.1 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from matplotlib->pymedphys==0.19.0dev0) (1.1.0)
Requirement already satisfied: six in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from packaging->pymedphys==0.19.0dev0) (1.13.0)
Requirement already satisfied: shapely in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from shapely-helper->pymedphys==0.19.0dev0) (1.6.4.post2)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from requests->pymedphys==0.19.0dev0) (1.25.7)
Requirement already satisfied: certifi>=2017.4.17 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from requests->pymedphys==0.19.0dev0) (2019.11.28)
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from requests->pymedphys==0.19.0dev0) (3.0.4)
Requirement already satisfied: idna<2.9,>=2.5 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from requests->pymedphys==0.19.0dev0) (2.8)
Requirement already satisfied: secretstorage; sys_platform == "linux" in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from keyring->pymedphys==0.19.0dev0) (3.1.1)
Requirement already satisfied: importlib-metadata; python_version < "3.8" in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from keyring->pymedphys==0.19.0dev0) (0.23)
Requirement already satisfied: decorator>=4.3.0 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from networkx>=2.0->scikit-image->pymedphys==0.19.0dev0) (4.4.1)
Requirement already satisfied: setuptools in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from kiwisolver>=1.0.1->matplotlib->pymedphys==0.19.0dev0) (41.2.0)
Requirement already satisfied: jeepney in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from secretstorage; sys_platform == "linux"->keyring->pymedphys==0.19.0dev0) (0.4.2)
Requirement already satisfied: cryptography in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from secretstorage; sys_platform == "linux"->keyring->pymedphys==0.19.0dev0) (2.8)
Requirement already satisfied: zipp>=0.5 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from importlib-metadata; python_version < "3.8"->keyring->pymedphys==0.19.0dev0) (0.6.0)
Requirement already satisfied: cffi!=1.11.3,>=1.8 in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from cryptography->secretstorage; sys_platform == "linux"->keyring->pymedphys==0.19.0dev0) (1.13.2)
Requirement already satisfied: more-itertools in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from zipp>=0.5->importlib-metadata; python_version < "3.8"->keyring->pymedphys==0.19.0dev0) (7.2.0)
Requirement already satisfied: pycparser in /home/simon/.pyenv/versions/3.7.5/lib/python3.7/site-packages (from cffi!=1.11.3,>=1.8->cryptography->secretstorage; sys_platform == "linux"->keyring->pymedphys==0.19.0dev0) (2.19)
In [3]:
import pymedphys
import pymedphys._wlutz.core
import pymedphys._wlutz.reporting
import pymedphys._vendor.pylinac.winstonlutz
In [4]:
pymedphys.__version__
Out[4]:
'0.19.0dev0'
In [5]:
def display_markdown(string):
    IPython.display.display(IPython.display.Markdown(string))
    
display_markdown('## Example heading')

Example heading

In [6]:
dicom_wlutz_paths = pymedphys.zip_data_paths('denis_wlutz_images.zip')
In [7]:
dicom_wlutz_paths
Out[7]:
[PosixPath('/home/simon/.pymedphys/data/denis_wlutz_images/RT000000.dcm'),
 PosixPath('/home/simon/.pymedphys/data/denis_wlutz_images/RT000001.dcm'),
 PosixPath('/home/simon/.pymedphys/data/denis_wlutz_images/RT000002.dcm'),
 PosixPath('/home/simon/.pymedphys/data/denis_wlutz_images/RT000003.dcm'),
 PosixPath('/home/simon/.pymedphys/data/denis_wlutz_images/RT000004.dcm'),
 PosixPath('/home/simon/.pymedphys/data/denis_wlutz_images/RT000005.dcm'),
 PosixPath('/home/simon/.pymedphys/data/denis_wlutz_images/RT000006.dcm'),
 PosixPath('/home/simon/.pymedphys/data/denis_wlutz_images/RT000007.dcm')]
In [8]:
dicom_files = [pydicom.read_file(str(path), force=True) for path in dicom_wlutz_paths]
In [9]:
dcm_header = dicom_files[0]
dcm_header
Out[9]:
(0008, 0008) Image Type                          CS: ['ORIGINAL', 'PRIMARY', 'PORTAL', 'ACQUIRED_DOSE']
(0008, 0012) Instance Creation Date              DA: '20191220'
(0008, 0013) Instance Creation Time              TM: '105030'
(0008, 0016) SOP Class UID                       UI: RT Image Storage
(0008, 0018) SOP Instance UID                    UI: 1.2.276.0.7230010.3.1.4.2614442722.3656.1578233219.487
(0008, 0020) Study Date                          DA: ''
(0008, 0023) Content Date                        DA: ''
(0008, 0030) Study Time                          TM: ''
(0008, 0033) Content Time                        TM: ''
(0008, 0050) Accession Number                    SH: ''
(0008, 0060) Modality                            CS: 'RTIMAGE'
(0008, 0064) Conversion Type                     CS: 'DI'
(0008, 0070) Manufacturer                        LO: 'Varian Medical Systems'
(0008, 0090) Referring Physician's Name          PN: ''
(0008, 1030) Study Description                   LO: 'Abdomen^abdomen (Adult)'
(0008, 1090) Manufacturer's Model Name           LO: 'Patient Verification'
(0010, 0010) Patient's Name                      PN: 'Anonymized4'
(0010, 0020) Patient ID                          LO: '4d21dbc4-303f-44c7-8286-4c0898b9fe47'
(0010, 0030) Patient's Birth Date                DA: ''
(0010, 0040) Patient's Sex                       CS: ''
(0012, 0062) Patient Identity Removed            CS: 'YES'
(0018, 1020) Software Version(s)                 LO: '2.21.14.0'
(0018, 5100) Patient Position                    CS: 'HFS'
(0020, 000d) Study Instance UID                  UI: 1.2.276.0.7230010.3.1.2.2614442722.3656.1578233219.485
(0020, 000e) Series Instance UID                 UI: 1.2.276.0.7230010.3.1.3.2614442722.3656.1578233219.486
(0020, 0010) Study ID                            SH: ''
(0020, 0011) Series Number                       IS: "6"
(0020, 0013) Instance Number                     IS: "11"
(0020, 0020) Patient Orientation                 CS: ''
(0020, 0052) Frame of Reference UID              UI: 1.2.276.0.7230010.3.1.4.2614442722.3656.1578233219.488
(0020, 1040) Position Reference Indicator        LO: ''
(0028, 0002) Samples per Pixel                   US: 1
(0028, 0004) Photometric Interpretation          CS: 'MONOCHROME2'
(0028, 0010) Rows                                US: 1190
(0028, 0011) Columns                             US: 1190
(0028, 0100) Bits Allocated                      US: 16
(0028, 0101) Bits Stored                         US: 16
(0028, 0102) High Bit                            US: 15
(0028, 0103) Pixel Representation                US: 0
(0028, 1040) Pixel Intensity Relationship        CS: 'LIN'
(0028, 1041) Pixel Intensity Relationship Sign   SS: 1
(0028, 1050) Window Center                       DS: "0.03177921684718"
(0028, 1051) Window Width                        DS: "0.06355843369437"
(0028, 1052) Rescale Intercept                   DS: "0"
(0028, 1053) Rescale Slope                       DS: "1.4558922873e-06"
(0028, 1054) Rescale Type                        LO: 'CU'
(3002, 0002) RT Image Label                      SH: 'MV_90_0a1'
(3002, 0004) RT Image Description                ST: '6xFFF [MV], 600 [MU/min] \r\nModeId: Dosimetry\r\nMode Type : Continuous\r\nOffset Corr. : true\r\nGain Corr. : true\r\nPixel Corr. : true\r\nAveraging : false\r\nIntegration : true\r\nReset Frames : 0\r\nAEC : false\r\nImage Median: 16\r\nHistogram ROI: (2147483647,2147483647),(2147483647,2147483647)\r\n'
(3002, 000a) Reported Values Origin              CS: 'ACTUAL'
(3002, 000c) RT Image Plane                      CS: 'NORMAL'
(3002, 000d) X-Ray Image Receptor Translation    DS: ['0.00117159574335', '-0.0179384008247', '-500.05512712295']
(3002, 000e) X-Ray Image Receptor Angle          DS: "0"
(3002, 0010) RT Image Orientation                DS: ['1', '0', '0', '0', '-1', '0']
(3002, 0011) Image Plane Pixel Spacing           DS: ['0.336', '0.336']
(3002, 0012) RT Image Position                   DS: ['-199.752', '199.752']
(3002, 0020) Radiation Machine Name              SH: 'TrueBeamSN3772'
(3002, 0022) Radiation Machine SAD               DS: "1000"
(3002, 0026) RT Image SID                        DS: "1500.05512712295"
(3002, 0029) Fraction Number                     IS: "0"
(3002, 0030)  Exposure Sequence   1 item(s) ---- 
   (0008, 1160) Referenced Frame Number             IS: "1"
   (0018, 0060) KVP                                 DS: "6000"
   (0018, 1150) Exposure Time                       IS: "5153"
   (3002, 0032) Meterset Exposure                   DS: "50.0458888774818"
   (300a, 00b6)  Beam Limiting Device Sequence   2 item(s) ---- 
      (300a, 00b8) RT Beam Limiting Device Type        CS: 'ASYMX'
      (300a, 00bc) Number of Leaf/Jaw Pairs            IS: "1"
      (300a, 011c) Leaf/Jaw Positions                  DS: ['-31.000003600769', '-10.000070432496']
      ---------
      (300a, 00b8) RT Beam Limiting Device Type        CS: 'ASYMY'
      (300a, 00bc) Number of Leaf/Jaw Pairs            IS: "1"
      (300a, 011c) Leaf/Jaw Positions                  DS: ['-30.999997601461', '-11.000001055814']
      ---------
   (300a, 00f0) Number of Blocks                    IS: "0"
   (300a, 011e) Gantry Angle                        DS: "89.9950238998863"
   (300a, 0120) Beam Limiting Device Angle          DS: "270.000213996296"
   (300a, 0122) Patient Support Angle               DS: "0.302734375"
   (300a, 0128) Table Top Vertical Position         DS: "-101.23138412199"
   (300a, 0129) Table Top Longitudinal Position     DS: "846.526877607933"
   (300a, 012a) Table Top Lateral Position          DS: "-11.996476525276"
   (300a, 0140) Table Top Pitch Angle               FL: 0.0020170211791992188
   (300a, 0144) Table Top Roll Angle                FL: 359.9999694824219
   ---------
(300a, 00b3) Primary Dosimeter Unit              CS: 'MU'
(300a, 011e) Gantry Angle                        DS: "89.9950238998863"
(300a, 0120) Beam Limiting Device Angle          DS: "270.000213996296"
(300a, 0122) Patient Support Angle               DS: "0.302734375"
(300a, 0128) Table Top Vertical Position         DS: "-101.23138412199"
(300a, 0129) Table Top Longitudinal Position     DS: "846.526877607933"
(300a, 012a) Table Top Lateral Position          DS: "-11.996476525276"
(300a, 012c) Isocenter Position                  DS: ['0', '0', '0']
(300a, 0140) Table Top Pitch Angle               FL: 0.0020170211791992188
(300a, 0144) Table Top Roll Angle                FL: 359.9999694824219
(300c, 0002)  Referenced RT Plan Sequence   1 item(s) ---- 
   (0008, 1150) Referenced SOP Class UID            UI: RT Plan Storage
   (0008, 1155) Referenced SOP Instance UID         UI: 1.2.276.0.7230010.3.1.4.2614442722.3656.1578233219.489
   ---------
(300c, 0006) Referenced Beam Number              IS: "9"
(300c, 0008) Start Cumulative Meterset Weight    DS: "4.5048735182e-06"
(300c, 0009) End Cumulative Meterset Weight      DS: "1.00092228242315"
(300c, 0022) Referenced Fraction Group Number    IS: "1"
(5000, 0005) Curve Dimensions                    US: 2
(5000, 0010) Number of Points                    US: 4
(5000, 0020) Type of Data                        CS: 'ROI'
(5000, 0030) Axis Units                          SH: ['PIXL', 'PIXL']
(5000, 0040) Axis Labels                         SH: ['Plan Field Edge', 'Plan Field Edge']
(5000, 0103) Data Value Representation           US: 3
(5000, 2500) Curve Label                         LO: 'Field Aperture'
(5000, 3000) Curve Data                          OB or OW: Array of 64 elements
(7fe0, 0010) Pixel Data                          OW: Array of 2832200 elements
In [10]:
dcm_header.ImagePlanePixelSpacing
Out[10]:
['0.336', '0.336']
In [11]:
sad = float(dcm_header.RadiationMachineSAD)
panel_adjustment = -float(dcm_header.XRayImageReceptorTranslation[2])
panel_ssd = panel_adjustment + sad

guess_at_pixel_spacing_at_iso = np.array(dcm_header.ImagePlanePixelSpacing).astype(float) / panel_ssd * sad
dx, dy = guess_at_pixel_spacing_at_iso

dx, dy
Out[11]:
(0.223991767985511, 0.223991767985511)
In [12]:
half_range_x = dcm_header.Columns * dy / 2
half_range_y = dcm_header.Rows * dx / 2
In [13]:
x = np.linspace(-half_range_x, half_range_x, dcm_header.Columns)
y = np.linspace(-half_range_y, half_range_y, dcm_header.Rows)
In [14]:
jaw_pos = {
    coll.RTBeamLimitingDeviceType: np.array(coll.LeafJawPositions).astype(float)
    for coll in dcm_header.ExposureSequence[0].BeamLimitingDeviceSequence
}

jaw_pos
Out[14]:
{'ASYMX': array([-31.0000036 , -10.00007043]),
 'ASYMY': array([-30.9999976 , -11.00000106])}
In [15]:
field_size_x = np.diff(jaw_pos['ASYMX'])[0]
field_size_y = np.diff(jaw_pos['ASYMY'])[0]

edge_lengths = [field_size_x, field_size_y]
In [16]:
penumbra = 2
bb_diameter = 8
In [17]:
gantries = np.array([
    np.round(dcm.GantryAngle, 2) for dcm in dicom_files
])

gantries
Out[17]:
array([ 90.  ,  90.  ,   0.  , 180.01,   0.  , 270.  , 270.  , 180.  ])
In [18]:
colls = np.array([
    np.round(dcm.BeamLimitingDeviceAngle, 2) for dcm in dicom_files
])

colls
Out[18]:
array([270.  ,  89.99,  89.99,  90.  , 270.  , 270.  ,  90.  , 270.  ])
In [19]:
images = [dcm.pixel_array for dcm in dicom_files]
scaled_images = [
    img[::-1, :] / 2 ** 16 for img in images
]
In [20]:
for img in scaled_images:
    plt.figure()
    plt.imshow(scaled_images[0])
    plt.colorbar()
In [21]:
def get_initial_centre(x, y, img):
    wl_image = pymedphys._vendor.pylinac.winstonlutz.WLImageOld(img)
    min_x = np.min(x)
    dx = x[1] - x[0]
    min_y = np.min(y)
    dy = y[1] - y[0]
    
    field_centre = [
        wl_image.field_cax.x * dx + min_x,
        wl_image.field_cax.y * dy + min_y
    ]
    
    return field_centre
In [25]:
def analysis(x, y, img, collimator_angle):
    field = pymedphys._wlutz.imginterp.create_interpolated_field(x, y, img)
    initial_centre = get_initial_centre(x, y, img)
    
    pymedphys._wlutz.reporting.image_analysis_figure(
        x,
        y,
        img,
        None,
        initial_centre,
        collimator_angle,
        bb_diameter,
        edge_lengths,
        penumbra,
    )
    
    plt.title('Initial Centre')

    field_centre, field_rotation = pymedphys._wlutz.findfield.field_centre_and_rotation_refining(
        field, edge_lengths, penumbra, initial_centre, pylinac_tol=np.inf, fixed_rotation=collimator_angle
    )    

    bb_centre = pymedphys._wlutz.findbb.optimise_bb_centre( 
        field, bb_diameter, edge_lengths, penumbra, field_centre, field_rotation, ignore_pylinac=True
    )
    
    pymedphys._wlutz.reporting.image_analysis_figure(
        x,
        y,
        img,
        bb_centre,
        field_centre,
        collimator_angle,
        bb_diameter,
        edge_lengths,
        penumbra,
    )

    plt.title('PyMedPhys Basinhopping Method')
    
    try:
        pylinac = pymedphys._wlutz.pylinac.run_wlutz(
            field, edge_lengths, penumbra, field_centre, collimator_angle)
    
        pymedphys._wlutz.reporting.image_analysis_figure(
            x,
            y,
            img,
            pylinac['v2.2.6']['bb_centre'],
            pylinac['v2.2.6']['field_centre'],
            collimator_angle,
            bb_diameter,
            edge_lengths,
            penumbra,
        )

        plt.title('Pylinac v2.2.6 Filter and Profile Method')

        pymedphys._wlutz.reporting.image_analysis_figure(
            x,
            y,
            img,
            pylinac['v2.2.7']['bb_centre'],
            pylinac['v2.2.7']['field_centre'],
            collimator_angle,
            bb_diameter,
            edge_lengths,
            penumbra,
        )

        plt.title('Pylinac v2.2.7 Filter and Scikit-Image Method')
    except Exception as e:
        print(e)
        
    return np.array(field_centre) - np.array(bb_centre)
In [30]:
for img, coll, gantry in zip(scaled_images, colls, gantries):
    display_markdown(f'## Gantry {coll} | Collimator {gantry}')
    
    deviation = np.round(analysis(x, y, img, coll), 2)
    display_markdown(
        f'PyMedPhys field centre - BB centre (mm):\n\n```python\n[x, y] = [{deviation[0]}, {deviation[1]}]\n```'
    )
    plt.show()

Gantry 270.0 | Collimator 90.0

PyMedPhys field centre - BB centre (mm):

[x, y] = [1.62, -0.27]

Gantry 89.99 | Collimator 90.0

PyMedPhys field centre - BB centre (mm):

[x, y] = [1.98, -0.29]

Gantry 89.99 | Collimator 0.0

PyMedPhys field centre - BB centre (mm):

[x, y] = [-0.63, -0.0]

Gantry 90.0 | Collimator 180.01

PyMedPhys field centre - BB centre (mm):

[x, y] = [0.46, -0.57]

Gantry 270.0 | Collimator 0.0

PyMedPhys field centre - BB centre (mm):

[x, y] = [-1.07, 0.31]

Gantry 270.0 | Collimator 270.0

PyMedPhys field centre - BB centre (mm):

[x, y] = [-2.86, 0.09]

Gantry 90.0 | Collimator 270.0

PyMedPhys field centre - BB centre (mm):

[x, y] = [-2.58, -0.23]

Gantry 270.0 | Collimator 180.0

PyMedPhys field centre - BB centre (mm):

[x, y] = [0.39, -0.45]